First-pass attempt at characterizing calcium spikes
# install.packages("Rcatch22")
# install.packages("dichromat")
# install.packages("DescTools")
library(Rcatch22)
library(smooth)
Loading required package: greybox
Package "greybox", v2.0.0 loaded.
This is package "smooth", v4.0.0
library(tidyverse)
── Attaching core tidyverse packages ────────────────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.4
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.4.4 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.0
✔ purrr 1.0.2 ── Conflicts ──────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ lubridate::hm() masks greybox::hm()
✖ dplyr::lag() masks stats::lag()
✖ tidyr::spread() masks greybox::spread()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
source("traceAnalysisFxns.r")
numRuns <- function(x, threshold=0.2,len=2)
{
x[x>=threshold]
out <- rle(x)$lengths
length(out[out>=len])
}
numSpikes <- function(x, threshold=0.17)
{
length(which(x>=threshold))
}
avgSpikeLength <- function(x, threshold=0.17)
{
pts_above_th <- x[x>=threshold]
out <- tryCatch({rle(pts_above_th)$lengths},error=function(cond){NA})
tryCatch({mean(out)},error=function(cond){message(cond);NA})
}
avgTimeBetweenSpikes <- function(x, threshold=0.17)
{
pts_above_th <- x[x>=threshold]
out <- tryCatch({inverse.rle(pts_above_th)$lengths},error=function(cond){NA})
tryCatch({mean(out)},error=function(cond){NA})
}
std_colnames <- c("reg_id","im_idx","time_sec","fura_ratio","expt_date")
fixColnames <- function(x) sapply(x, function(z)
switch(z,
ND.T="im_idx",
"ROI ID" = "reg_id",
totalSec = "time_sec",
Fura_Ratio = "fura_ratio",
ratio_340_380 = "fura_ratio",
"Ratio 340/380" = "fura_ratio",
ROI = "reg_id",
z))
convertTime <- function(dat) {
cn <- colnames(dat)
if("Time_ms" %in% cn & ! "time_sec" %in% cn) {
dat <- dat %>% separate(Time_ms, c("Minute", "Second"), sep = ":", convert = TRUE) %>%
mutate(time_sec = Minute*60 + Second) %>% select(all_of(setdiff(c(cn,"time_sec"),c("Time_ms","Minute","Second"))))
} else {
dat
}
}
processRawXLSX <- function(fpath, filter_min_area=100) {
stopifnot(any(grepl("\\.xlsx", fpath)))
expt_date <- substr(sapply(strsplit(fpath, "/"), "[[", 7),1,8)
cond <- NA
if(any(grepl("csv", fpath))) cond_temp <- sapply(strsplit(sapply(strsplit(fpath, "/"), "[[", 7), "\\.csv\\?"), "[[", 1)
if(any(grepl("xlsx", fpath))) cond_temp <- sapply(strsplit(sapply(strsplit(fpath, "/"), "[[", 7), "\\.xlsx\\?"), "[[", 1)
cond_pt1 <- sapply(strsplit(cond_temp, "_"), "[[", 2)
cond_pt2 <- sapply(strsplit(cond_temp, "_"), "[[", 3)
cond_pt3 <- ifelse(length(strsplit(cond_temp, "_")) >= 4, sapply(strsplit(cond_temp, "_"), "[[", 4), "")
cond_temp <- paste(cond_pt1, cond_pt2, cond_pt3, sep="_")
if(any(grepl("naive", cond_temp))) {
naive_temp <- c(cond_pt1,cond_pt2,cond_pt3)[grep("naive",c(cond_pt1,cond_pt2,cond_pt3))]
cond <- paste(expt_date, naive_temp, sep="_")
}
if(any(grepl("untreated", cond_temp)))
cond <- paste(expt_date, "naive", sep="_")
if(any(grepl("PLX",cond_temp)))
cond <- paste(expt_date, sapply(strsplit(cond_temp, "_"), "[[", 3), sep="_")
if(any(grepl("free",cond_temp)))
cond <- paste(expt_date, "3day8uMPLXCa2free", sep="_")
if(any(grepl("Ca2containing",cond_temp)))
cond <- paste(expt_date, "3day8uMPLX_w_Ca2", sep="_")
if(any(grepl("MEK",cond_temp)))
cond <- paste(expt_date, "30minMEKi", sep="_")
if(any(grepl("dropbox",fpath))) {
temp <- tempfile()
download.file(fpath, temp, mode="wb")
} else {
temp <- fpath
}
dat <- readxl::read_xlsx(temp)
keep_ocols <- c("Time [m:s]","ND.T","ROI ID","Intensity(340)","StDev(340)","Intensity(380)","StDev(380)","ROI Area [µm²]","Ratio 340/380")
dat <- dat[,keep_ocols]
times <- unique(dat$`Time [m:s]`)
diff_times <- difftime(times, times[1], units="secs", tz="UTC")
dat$time_sec <- DescTools::RoundTo(diff_times[match(dat$`Time [m:s]`,times)], multiple=5)
# remove objects < 100 µM^2
dat <- dat[dat$`ROI Area [µm²]` > filter_min_area,]
# fix colnames
colnames(dat) <- fixColnames(colnames(dat))
dat$expt_date <- expt_date
dat$cond <- cond
return(as.data.frame(dat[,c(std_colnames,"cond")]))
}
preprocessData <- function(fpath) {
expt_date <- substr(sapply(strsplit(fpath, "/"), "[[", 7),1,8)
cond_temp <- sapply(strsplit(sapply(strsplit(fpath, "/"), "[[", 7), "\\.csv\\?"), "[[", 1)
cond_pt1 <- sapply(strsplit(cond_temp, "_"), "[[", 2)
cond_pt2 <- sapply(strsplit(cond_temp, "_"), "[[", 3)
cond_pt3 <- sapply(strsplit(cond_temp, "_"), "[[", 4)
cond_temp <- paste(cond_pt1, cond_pt2, cond_pt3, sep="_")
if(any(grepl("naive", cond_temp)))
cond <- paste(expt_date, "naive", sep="_")
if(any(grepl("untreated", cond_temp)))
cond <- paste(expt_date, "naive", sep="_")
if(any(grepl("PLX",cond_temp)))
cond <- paste(expt_date, sapply(strsplit(cond_temp, "_"), "[[", 3), sep="_")
if(any(grepl("free",cond_temp)))
cond <- paste(expt_date, "8day8uMPLXCa2free", sep="_")
if(any(grepl("Ca2containing",cond_temp)))
cond <- paste(expt_date, "3day8uMPLX_w_Ca2", sep="_")
if(any(grepl("MEK",cond_temp)))
cond <- paste(expt_date, "30minMEKi", sep="_")
d <- read.csv(fpath)
d <- convertTime(d)
colnames(d) <- fixColnames(colnames(d))
d <- d[,intersect(std_colnames,colnames(d))]
d$expt_date <- expt_date
d$cond <- cond
return(d)
}
sampleROIs <- function(condition, ids=rois, n=100) sample(ids[grep(condition,ids)],n)
plotPCcatch <- function(roi, group_ids=clust$cluster, ...) {
groups <- unique(group_ids)
mycolors <- dichromat::colorschemes$Categorical.12[seq_along(groups)]
plot(pc_catch[['x']][,1], pc_catch[['x']][,2], type="n",
xlab="PC1", ylab="PC2", ...)
sapply(groups, function(group) points(pc_catch[['x']][which(group_ids==group & rois %in% roi),1],
pc_catch[['x']][which(group_ids==group & rois %in% roi),2],
col=mycolors[match(group,groups)],
pch=19, cex=0.5,))
return(invisible(NULL))
}
Datasets
20220425 = naive
20230426 = untreated
20211114 = tolerant? 3 days?
20220321_fura2_A375H2bRFP_4day8uMPLX
20220321_fura2_A375H2bRFP_5day8uMPLX
20230426_idlingCa2free_pulsing002_V02
fpaths <- c("https://www.dropbox.com/scl/fi/yprt6h4s2khr7sk0kaoav/20230426_untreated_pulsing_mod.csv?rlkey=bt575yvb612cgf1bi23fb9n99&dl=1",
# "https://www.dropbox.com/scl/fi/q5gxqbib7sjkbnzrjsl2q/20220425_Fura2_naive_pulsing_mod.csv?rlkey=9frhbyh7urxdm8s6h1k8n5vut&dl=1",
"https://www.dropbox.com/scl/fi/fk0fh1trr46aromfqd31k/20220321_fura2_A375H2bRFP_5day8uMPLX.csv?rlkey=jhhm821t8q63c3n6yb6udasxg&dl=1",
"https://www.dropbox.com/scl/fi/r0bitvu4ljz19jah9f9p8/20220321_fura2_A375H2bRFP_7day8uMPLX.csv?rlkey=i6wnyo48z5a0v4ovsrgysz2xm&dl=1",
"https://www.dropbox.com/scl/fi/9j6dvk9vnar3cn5xe24he/20220321_fura2_A375H2bRFP_10day8uMPLX.csv?rlkey=62l5qpih4oetwrte0k3bk3szr&dl=1",
"https://www.dropbox.com/scl/fi/6pq4d62ser0bcsuvwfcpv/20230426_idlingCa2free_pulsing002_V02_mod.csv?rlkey=k19wxifmfh3rn69zdgn0kb3tc&dl=1"
)
rawdatapaths <- c(
"https://www.dropbox.com/scl/fi/f0ap9tuwfldtrjk2pd3yn/20220425_Fura2_naive002.xlsx?rlkey=dx1zwj6ov8zwzqt944jwc05z9&dl=1",
"https://www.dropbox.com/scl/fi/ru1cunsuqgi691ua05dgs/20220425_Fura2_naive003.xlsx?rlkey=f1q7uh454sqjspmr3anabdy1k&dl=1"
)
fp <- "https://www.dropbox.com/s/elwck3cnpzvg4uf/20211114_A375_Ca2%2B_pulsing_data.csv?dl=1"
d <- read.csv(fp)
colnames(d) <- c("reg_id", "fura_ratio", "time_sec", "time_min")
d$expt_date <- "20211114"
d$im_idx <- match(d$time_sec, sort(unique(d$time_sec)))
colnames(d) <- fixColnames(colnames(d))
d <- d[,intersect(std_colnames,colnames(d))]
d$cond <- "20211114_14day8uMPLX"
d <- rbind(d, do.call(rbind, lapply(fpaths, preprocessData)))
d <- rbind(d, do.call(rbind, lapply(rawdatapaths, processRawXLSX)))
trying URL 'https://www.dropbox.com/scl/fi/f0ap9tuwfldtrjk2pd3yn/20220425_Fura2_naive002.xlsx?rlkey=dx1zwj6ov8zwzqt944jwc05z9&dl=1'
Content type 'application/binary' length 18257512 bytes (17.4 MB)
==================================================
downloaded 17.4 MB
trying URL 'https://www.dropbox.com/scl/fi/ru1cunsuqgi691ua05dgs/20220425_Fura2_naive003.xlsx?rlkey=f1q7uh454sqjspmr3anabdy1k&dl=1'
Content type 'application/binary' length 16680046 bytes (15.9 MB)
==================================================
downloaded 15.9 MB
# calculate/adjust columns
d$reg_id <- paste(d$cond, d$reg_id, sep="_")
d$intensity_mean <- d$fura_ratio
# ensure all times are a multiple of 5
d$time_sec <- DescTools::RoundTo(d$time_sec, multiple=5)
NOTE
Experiments are each of different duration. To facilitate
interpretation, use number of time points of the shortest dataset for
each
expt_dates <- unique(d$expt_date)
conditions <- unique(d$cond)
n_im_idx <- min(sapply(conditions, function(co) length(unique((d[d$cond==co,"im_idx"])))))
temp <- lapply(unique(d$cond), function(co) {
a <- d[d$cond==co,]
a <- a[a$time_sec %in% tail(unique(a$time_sec), n_im_idx),]
a$time_sec <- DescTools::RoundTo(a$time_sec - min(a$time_sec), multiple=5)
# a$time_sec <- seq(0,((n_im_idx-1)*5),5)
a$im_idx <- match(a$time_sec, sort(unique(a$time_sec)))
rownames(a) <- NULL
return(a)
})
d <- do.call(rbind, temp)
rois <- unique(d$reg_id)
times <- unique(d$time_sec)
# fr = fura2_ratio
fr <- data.frame(matrix(d$fura_ratio, ncol=length(rois)))
colnames(fr) <- as.character(rois)
# linear model - intersect
l <- do.call(c, lapply(conditions, function(co) {
apply(fr[,grepl(co,colnames(fr))], 2, function(x) coef(lm(x ~ times))[2])
}))
# mean fura_ratio
fr_mean <- do.call(cbind,lapply(conditions, function(co) {
apply(fr[,grepl(co,colnames(fr))], 1, mean)
}))
colnames(fr_mean) <- conditions
# frn = fura2_ratio normalized (mean-centered)
frn <- do.call(cbind, lapply(conditions, function(co) {
apply(fr[,grepl(co,colnames(fr))], 2, function(x) x - fr_mean[,match(co,conditions)])
}))
d$fura_ratio_norm <- as.vector(frn)
d$int_mean_norm <- as.vector(frn)
Things to consider
- DTW
- Sum of time above threshold
- Stretches of times above threshold
- Times between spikes
- Number of spikes
- Determine the background for each trace
interesting ROIs: 11, 12, 18, 20, 25, 27
checkthese <- paste("20211114_14day8uMPLX", c(11, 12, 18, 20, 25, 27), sep="_")
plot(fura_ratio ~ time_sec, data=d[d$reg_id %in% checkthese,], type="n", ylim=c(0,1))
for(roi in checkthese) lines(fura_ratio ~ time_sec, data=d[d$reg_id==roi,], col=dichromat::colorschemes$Categorical.12[match(roi,checkthese)])

spike_height <- sd(d$fura_ratio_norm)*2.5
roi_list <- lapply(unique(d$reg_id), function(roi)
{
dat <- d[d$reg_id==roi,"fura_ratio_norm"]
thresh <- .15
out <- data.frame(reg_id=roi,
mean=mean(dat),
sd=sd(dat),
avg.spike.length=avgSpikeLength(dat, threshold=thresh),
num.runs=numRuns(dat, threshold=thresh),
num.spikes=numSpikes(dat,threshold=thresh),
slope=coef(lm(dat ~ times))[2],
intercept=coef(lm(dat ~ times))[1]
)
rownames(out) <- NULL
return(out)
})
sumstats <- do.call(rbind, roi_list)
sumstats$avg.spike.length[is.na(sumstats$avg.spike.length)] <- 0
catch <- lapply(unique(d$reg_id), function(roi)
{
dat <- catch22_all(d[d$reg_id==roi,"fura_ratio_norm"])
dat <- as.data.frame(dat)
rownames(dat) <- dat[,1]
dat$names <- NULL
dat <- t(dat)
return(dat)
})
Warning: As of 0.1.14 the feature 'CO_f1ecac' returns a double instead of int
pc_catch <- prcomp(scale(catch))
plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5)

biplot(pc_catch, cex=0.5)

sumstats_scaled <- sumstats
sumstats_scaled[,-1] <- scale(sumstats_scaled[,-1])
lo_signal <- subset(sumstats, mean<0.05 & sd<0.01)
lo_ids <- lo_signal$reg_id
message(paste("There are",nrow(lo_signal),"cells with very low activity"))
There are 1942 cells with very low activity
Catch-22 variables
Location of low-signal traces in Catch-22 PCA space
plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5, xlab="PC1", ylab="PC2")
points(pc_catch[['x']][lo_signal$reg_id,1], pc_catch[['x']][lo_signal$reg_id,2], pch=19, cex=0.5, col="yellow")

Boundaries of Catch-22 PCA space
IDs on the boundaries (manually identified from biplot above) 604,
522, 210, 870, 946, 539, 542, 744, 675
eps_val <- 2
clust <- dbscan::dbscan(pc_catch[['x']], eps=eps_val, minPts = 20)
mycols <- rev(dichromat::colorschemes$Categorical.12)[seq(length(unique(clust$cluster)))]
plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5,
col=mycols[clust$cluster+1],
xlab="PC1", ylab="PC2")
mtext(paste("eps=",eps_val,"produced",length(unique(clust$cluster)),"clusters"), 3)
legend(-12,6,legend=paste("clust",seq(length(unique(clust$cluster)))), col=mycols, pch=19, cex=0.5)

message(cat("With eps=",eps_val,",",length(unique(clust$cluster)), "clusters were identified"))
With eps= 2 , 2 clusters were identified
diprate::nEach(clust$cluster)
0 1
1501 4111
No spikes looks like cluster==1
set.seed(10)
checkthese <- sample(rois[clust$cluster==1], 15)
plotTraceFura(checkthese, d)

Compare to lo_ids
plotTraceFura(lo_ids[1:15], d)

all_no_spike_ids <- union(rois[clust$cluster==1],lo_ids)
length(all_no_spike_ids)
[1] 4211
others (with spikes?) cluster==0
set.seed(6)
checkthese <- sample(rois[clust$cluster==0], 60)
plotTraceFura(checkthese[1:15], d)

Criteria
Philip described a few basic types of traces he sees:
- no spikes
- periodic spiking (oscillatory)
- sporadic individual spiking (rare and random)
- sporadic sustained spiking
- sustained activation (two-level trace)
Algorithms for classification
Fast fourier transform to detect regular oscillatory patterns
f <- mvfft(as.matrix(frn))
oscil <- sapply(rois, function(roi) any(Re(f[,as.character(roi)])[3:(nrow(f)-3)] > 10))
roi_oscil <- rois[oscil]
plot(seq(2,nrow(f)-1), tail(head(Re(f[,roi_oscil[2]]),-1),-1), type='l', ylim=c(-1,20))

Example oscillatory traces
plotTraceFura(roi_oscil[11:20], d)

plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5,
col=c("blue","orange")[(sumstats$reg_id %in% roi_oscil)+1],
xlab="PC1", ylab="PC2")

Example traces with hi mean values
hi <- sumstats_scaled[sumstats_scaled$mean >=0.1 & sumstats_scaled$sd <= 0,"reg_id"]
plotTraceFura(hi[1:10], d)

Single spikes
single_spike <- sumstats[sumstats$num.spikes==1,"reg_id"]
mycols3 <- viridisLite::viridis(n=38)
plotTraceFura(single_spike[1:20], d)

hi_slope <- rois[which(l>4e-5)]
plotTraceFura(hi_slope[1:10], d)

plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5,
col=c("blue","orange")[(sumstats$reg_id %in% hi_slope)+1],
xlab="PC1", ylab="PC2")

plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5, xlab="PC1", ylab="PC2")
points(pc_catch[['x']][single_spike,1], pc_catch[['x']][single_spike,2], pch=19, cex=0.5, col="yellow")

Expt conditions
plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=".", cex=0.5,
col=rev(dichromat::colorschemes$Categorical.12)[match(gsub("_[0-9]{1,3}$", "", sumstats$reg_id),conditions)],
xlab="PC1", ylab="PC2")
legend(-13,8, conditions, col=dichromat::colorschemes$Categorical.12[seq_along(conditions)], cex=0.5, pch=19)

set.seed(13)
my_samples <- invisible(lapply(conditions, function(cond) {
a <- sampleROIs(condition=cond)
plotPCcatch(a, clust$cluster, main=cond)
a
}))








set.seed(13)
my_samples <- invisible(lapply(conditions, function(cond) {
a <- sampleROIs(condition=cond)
plotPCcatch(a, as.integer(!rois %in% all_no_spike_ids), main=cond)
a
}))








lapply(seq_along(my_samples), function(i) plotTraceFura(my_samples[[i]][!my_samples[[i]] %in% all_no_spike_ids][1:20], d))
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








Example traces
Figure 1B
exemplar_ids <- c( "20211114_14day8uMPLX_105",
"20211114_14day8uMPLX_802",
"20211114_14day8uMPLX_297",
"20220321_7day8uMPLX_340",
"20220321_7day8uMPLX_1121",
"20211114_14day8uMPLX_604",
"20211114_14day8uMPLX_20"
)
spike_examples <- d[d$reg_id %in% exemplar_ids, ]
spike_examples$reg_id <- factor(spike_examples$reg_id, levels=exemplar_ids)
plotTraceFura(exemplar_ids, spike_examples, yl=c(0.5,1.25))

# ggsave("example_fura2_traces.png", device="png", width=5, height=3.5, units="in")
Plot % in each cluster for each condition
Perform bootstrapping to get sample statistics. Assessing proportions
of cells in clusters 0 and 1
# filtered objects in naive data (should filter all datasets) with area > 100 µM^2
set.seed(13)
prop_spiking <- do.call(c, lapply(conditions, function(co) {
samp <- lapply(1:100, function(i) sample(rois[grepl(co,rois)],200, replace=TRUE))
sapply(samp, function(x) length(which(!x %in% all_no_spike_ids))/200)
}))
prop_spiking_df <- data.frame(condition=rep(conditions, each=100),
prop_spiking=prop_spiking)
prop_spiking_cond_df <- prop_spiking_df
prop_spiking_cond_df$date <- sapply(strsplit(prop_spiking_cond_df$condition, "_"), "[[", 1)
prop_spiking_cond_df$condition <- sapply(strsplit(prop_spiking_cond_df$condition, "_"), "[[", 2)
prop_spiking_cond_df$condition <- gsub("day8uMPLX","",prop_spiking_cond_df$condition)
prop_spiking_cond_df[grep("naive",prop_spiking_cond_df$condition),"condition"] <- 0
prop_spiking_cond_df$condition <- factor(prop_spiking_cond_df$condition, levels=c("0", "5","7","10","14"))
prop_spiking_cond_df <- prop_spiking_cond_df[prop_spiking_cond_df$date != "20230426",]
Time dependence of spiking behavior
Figure 1C
p <- ggplot(data=prop_spiking_cond_df, aes(x=condition, y=prop_spiking, fill=1)) +
geom_violin(width=.75) + geom_boxplot(width=0.1, color="grey", alpha=0.5) +
# coord_flip() +
theme(legend.position="none",
axis.text.x = element_text(size=16),
axis.text.y = element_text(size=16),
axis.title.x = element_text(size=18, face="bold"),
axis.title.y = element_text(size=18, face="bold")) +
ylim(c(0,0.75)) +
ylab("Proportion spiking") +
xlab("Days in BRAFi")
p

# ggsave("BRAFi_time_prop_spiking.png", device="png", width=4, height=4, units="in")
m <- lm(prop_spiking ~ condition, data=prop_spiking_cond_df)
a <- aov(m)
tukey <- TukeyHSD(a)
tukey$condition
diff lwr upr p adj
5-0 0.075075 0.065782529 0.08436747 4.747650e-10
7-0 0.204875 0.195582529 0.21416747 4.747650e-10
10-0 0.209325 0.200032529 0.21861747 4.747650e-10
14-0 0.531025 0.521732529 0.54031747 4.747650e-10
7-5 0.129800 0.119069979 0.14053002 4.747650e-10
10-5 0.134250 0.123519979 0.14498002 4.747650e-10
14-5 0.455950 0.445219979 0.46668002 4.747650e-10
10-7 0.004450 -0.006280021 0.01518002 7.880375e-01
14-7 0.326150 0.315419979 0.33688002 4.747650e-10
14-10 0.321700 0.310969979 0.33243002 4.747650e-10
Number of cells per condition
sapply(conditions, function(co) length(unique(rois[grep(co,rois)])))
20211114_14day8uMPLX 20230426_naive 20220321_5day8uMPLX 20220321_7day8uMPLX 20220321_10day8uMPLX
882 224 1202 1141 1152
20230426_8day8uMPLXCa2free 20220425_naive002 20220425_naive003
266 383 362
LS0tCnRpdGxlOiAiQ2FjbGl1bSBwdWxzaW5nIGFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZGF0ZTogMjAyMy0wNC0yMwphdXRob3I6IERhcnJlbiBUeXNvbiAmIFBoaWxpcCBTdGF1ZmZlcgotLS0KIyMgRmlyc3QtcGFzcyBhdHRlbXB0IGF0IGNoYXJhY3Rlcml6aW5nIGNhbGNpdW0gc3Bpa2VzCgpgYGB7ciBMb2FkIHBhY2thZ2VzIGFuZCBmdW5jdGlvbnN9CiMgaW5zdGFsbC5wYWNrYWdlcygiUmNhdGNoMjIiKQojIGluc3RhbGwucGFja2FnZXMoImRpY2hyb21hdCIpCiMgaW5zdGFsbC5wYWNrYWdlcygiRGVzY1Rvb2xzIikKCmxpYnJhcnkoUmNhdGNoMjIpCmxpYnJhcnkoc21vb3RoKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKc291cmNlKCJ0cmFjZUFuYWx5c2lzRnhucy5yIikKCm51bVJ1bnMgPC0gZnVuY3Rpb24oeCwgdGhyZXNob2xkPTAuMixsZW49MikgCnsKICAgIHhbeD49dGhyZXNob2xkXQogICAgb3V0IDwtIHJsZSh4KSRsZW5ndGhzCiAgICBsZW5ndGgob3V0W291dD49bGVuXSkKfQoKbnVtU3Bpa2VzIDwtIGZ1bmN0aW9uKHgsIHRocmVzaG9sZD0wLjE3KQp7CiAgICBsZW5ndGgod2hpY2goeD49dGhyZXNob2xkKSkKfQphdmdTcGlrZUxlbmd0aCA8LSBmdW5jdGlvbih4LCB0aHJlc2hvbGQ9MC4xNykgCnsKICAgIHB0c19hYm92ZV90aCA8LSB4W3g+PXRocmVzaG9sZF0KICAgIG91dCA8LSB0cnlDYXRjaCh7cmxlKHB0c19hYm92ZV90aCkkbGVuZ3Roc30sZXJyb3I9ZnVuY3Rpb24oY29uZCl7TkF9KQogICAgdHJ5Q2F0Y2goe21lYW4ob3V0KX0sZXJyb3I9ZnVuY3Rpb24oY29uZCl7bWVzc2FnZShjb25kKTtOQX0pCn0KCmF2Z1RpbWVCZXR3ZWVuU3Bpa2VzIDwtIGZ1bmN0aW9uKHgsIHRocmVzaG9sZD0wLjE3KQp7CiAgICBwdHNfYWJvdmVfdGggPC0geFt4Pj10aHJlc2hvbGRdCiAgICBvdXQgPC0gdHJ5Q2F0Y2goe2ludmVyc2UucmxlKHB0c19hYm92ZV90aCkkbGVuZ3Roc30sZXJyb3I9ZnVuY3Rpb24oY29uZCl7TkF9KQogICAgdHJ5Q2F0Y2goe21lYW4ob3V0KX0sZXJyb3I9ZnVuY3Rpb24oY29uZCl7TkF9KQp9CgoKc3RkX2NvbG5hbWVzIDwtIGMoInJlZ19pZCIsImltX2lkeCIsInRpbWVfc2VjIiwiZnVyYV9yYXRpbyIsImV4cHRfZGF0ZSIpCgpmaXhDb2xuYW1lcyA8LSBmdW5jdGlvbih4KSBzYXBwbHkoeCwgZnVuY3Rpb24oeikgCiAgICBzd2l0Y2goeiwKICAgICAgICAgICBORC5UPSJpbV9pZHgiLAogICAgICAgICAgICJST0kgSUQiID0gInJlZ19pZCIsCiAgICAgICAgICAgdG90YWxTZWMgPSAidGltZV9zZWMiLAogICAgICAgICAgIEZ1cmFfUmF0aW8gPSAiZnVyYV9yYXRpbyIsCiAgICAgICAgICAgcmF0aW9fMzQwXzM4MCA9ICJmdXJhX3JhdGlvIiwKICAgICAgICAgICAiUmF0aW8gMzQwLzM4MCIgPSAiZnVyYV9yYXRpbyIsCiAgICAgICAgICAgUk9JID0gInJlZ19pZCIsCiAgICAgICAgICAgeikpCgpjb252ZXJ0VGltZSA8LSBmdW5jdGlvbihkYXQpIHsKICAgIGNuIDwtIGNvbG5hbWVzKGRhdCkKICAgIGlmKCJUaW1lX21zIiAlaW4lIGNuICYgISAidGltZV9zZWMiICVpbiUgY24pIHsKICAgICAgICBkYXQgPC0gZGF0ICU+JSBzZXBhcmF0ZShUaW1lX21zLCBjKCJNaW51dGUiLCAiU2Vjb25kIiksIHNlcCA9ICI6IiwgY29udmVydCA9IFRSVUUpICU+JSAKICAgICAgICAgICAgbXV0YXRlKHRpbWVfc2VjID0gTWludXRlKjYwICsgU2Vjb25kKSAgJT4lIHNlbGVjdChhbGxfb2Yoc2V0ZGlmZihjKGNuLCJ0aW1lX3NlYyIpLGMoIlRpbWVfbXMiLCJNaW51dGUiLCJTZWNvbmQiKSkpKQogICAgfSBlbHNlIHsKICAgICAgICBkYXQKICAgIH0KfQoKcHJvY2Vzc1Jhd1hMU1ggPC0gZnVuY3Rpb24oZnBhdGgsIGZpbHRlcl9taW5fYXJlYT0xMDApIHsKICAgIHN0b3BpZm5vdChhbnkoZ3JlcGwoIlxcLnhsc3giLCBmcGF0aCkpKQogICAgCiAgICBleHB0X2RhdGUgPC0gc3Vic3RyKHNhcHBseShzdHJzcGxpdChmcGF0aCwgIi8iKSwgIltbIiwgNyksMSw4KQogICAgY29uZCA8LSBOQQoKICAgIGlmKGFueShncmVwbCgiY3N2IiwgZnBhdGgpKSkgY29uZF90ZW1wIDwtIHNhcHBseShzdHJzcGxpdChzYXBwbHkoc3Ryc3BsaXQoZnBhdGgsICIvIiksICJbWyIsIDcpLCAiXFwuY3N2XFw/IiksICJbWyIsIDEpCiAgICBpZihhbnkoZ3JlcGwoInhsc3giLCBmcGF0aCkpKSBjb25kX3RlbXAgPC0gc2FwcGx5KHN0cnNwbGl0KHNhcHBseShzdHJzcGxpdChmcGF0aCwgIi8iKSwgIltbIiwgNyksICJcXC54bHN4XFw/IiksICJbWyIsIDEpCgogICAgY29uZF9wdDEgPC0gc2FwcGx5KHN0cnNwbGl0KGNvbmRfdGVtcCwgIl8iKSwgIltbIiwgMikKICAgIGNvbmRfcHQyIDwtIHNhcHBseShzdHJzcGxpdChjb25kX3RlbXAsICJfIiksICJbWyIsIDMpCiAgICBjb25kX3B0MyA8LSBpZmVsc2UobGVuZ3RoKHN0cnNwbGl0KGNvbmRfdGVtcCwgIl8iKSkgPj0gNCwgc2FwcGx5KHN0cnNwbGl0KGNvbmRfdGVtcCwgIl8iKSwgIltbIiwgNCksICIiKQogICAgY29uZF90ZW1wIDwtIHBhc3RlKGNvbmRfcHQxLCBjb25kX3B0MiwgY29uZF9wdDMsIHNlcD0iXyIpCgogICAgaWYoYW55KGdyZXBsKCJuYWl2ZSIsIGNvbmRfdGVtcCkpKSB7CiAgICAgICAgbmFpdmVfdGVtcCA8LSBjKGNvbmRfcHQxLGNvbmRfcHQyLGNvbmRfcHQzKVtncmVwKCJuYWl2ZSIsYyhjb25kX3B0MSxjb25kX3B0Mixjb25kX3B0MykpXQogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCBuYWl2ZV90ZW1wLCBzZXA9Il8iKQogICAgfQogICAgaWYoYW55KGdyZXBsKCJ1bnRyZWF0ZWQiLCBjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICJuYWl2ZSIsIHNlcD0iXyIpCiAgICBpZihhbnkoZ3JlcGwoIlBMWCIsY29uZF90ZW1wKSkpIAogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCBzYXBwbHkoc3Ryc3BsaXQoY29uZF90ZW1wLCAiXyIpLCAiW1siLCAzKSwgc2VwPSJfIikKICAgIGlmKGFueShncmVwbCgiZnJlZSIsY29uZF90ZW1wKSkpIAogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCAiM2RheTh1TVBMWENhMmZyZWUiLCBzZXA9Il8iKQogICAgaWYoYW55KGdyZXBsKCJDYTJjb250YWluaW5nIixjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICIzZGF5OHVNUExYX3dfQ2EyIiwgc2VwPSJfIikKICAgIGlmKGFueShncmVwbCgiTUVLIixjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICIzMG1pbk1FS2kiLCBzZXA9Il8iKQoKICAgIGlmKGFueShncmVwbCgiZHJvcGJveCIsZnBhdGgpKSkgewogICAgICAgIHRlbXAgPC0gdGVtcGZpbGUoKQogICAgICAgIGRvd25sb2FkLmZpbGUoZnBhdGgsIHRlbXAsIG1vZGU9IndiIikKICAgIH0gZWxzZSB7CiAgICAgICAgdGVtcCA8LSBmcGF0aAogICAgfQogICAgZGF0IDwtIHJlYWR4bDo6cmVhZF94bHN4KHRlbXApCiAgICBrZWVwX29jb2xzIDwtIGMoIlRpbWUgW206c10iLCJORC5UIiwiUk9JIElEIiwiSW50ZW5zaXR5KDM0MCkiLCJTdERldigzNDApIiwiSW50ZW5zaXR5KDM4MCkiLCJTdERldigzODApIiwiUk9JIEFyZWEgW8K1bcKyXSIsIlJhdGlvIDM0MC8zODAiKQogICAgZGF0IDwtIGRhdFssa2VlcF9vY29sc10KICAgIHRpbWVzIDwtIHVuaXF1ZShkYXQkYFRpbWUgW206c11gKQogICAgZGlmZl90aW1lcyA8LSBkaWZmdGltZSh0aW1lcywgdGltZXNbMV0sIHVuaXRzPSJzZWNzIiwgdHo9IlVUQyIpCiAgICBkYXQkdGltZV9zZWMgPC0gRGVzY1Rvb2xzOjpSb3VuZFRvKGRpZmZfdGltZXNbbWF0Y2goZGF0JGBUaW1lIFttOnNdYCx0aW1lcyldLCBtdWx0aXBsZT01KQogICAgIyByZW1vdmUgb2JqZWN0cyA8IDEwMCDCtU1eMgogICAgZGF0IDwtIGRhdFtkYXQkYFJPSSBBcmVhIFvCtW3Csl1gID4gZmlsdGVyX21pbl9hcmVhLF0KICAgIAogICAgIyBmaXggY29sbmFtZXMKICAgIGNvbG5hbWVzKGRhdCkgPC0gZml4Q29sbmFtZXMoY29sbmFtZXMoZGF0KSkKICAgIGRhdCRleHB0X2RhdGUgPC0gZXhwdF9kYXRlCiAgICBkYXQkY29uZCA8LSBjb25kCiAgICByZXR1cm4oYXMuZGF0YS5mcmFtZShkYXRbLGMoc3RkX2NvbG5hbWVzLCJjb25kIildKSkKfQoKcHJlcHJvY2Vzc0RhdGEgPC0gZnVuY3Rpb24oZnBhdGgpIHsKICAgIGV4cHRfZGF0ZSA8LSBzdWJzdHIoc2FwcGx5KHN0cnNwbGl0KGZwYXRoLCAiLyIpLCAiW1siLCA3KSwxLDgpCgogICAgY29uZF90ZW1wIDwtIHNhcHBseShzdHJzcGxpdChzYXBwbHkoc3Ryc3BsaXQoZnBhdGgsICIvIiksICJbWyIsIDcpLCAiXFwuY3N2XFw/IiksICJbWyIsIDEpCiAgICBjb25kX3B0MSA8LSBzYXBwbHkoc3Ryc3BsaXQoY29uZF90ZW1wLCAiXyIpLCAiW1siLCAyKQogICAgY29uZF9wdDIgPC0gc2FwcGx5KHN0cnNwbGl0KGNvbmRfdGVtcCwgIl8iKSwgIltbIiwgMykKICAgIGNvbmRfcHQzIDwtIHNhcHBseShzdHJzcGxpdChjb25kX3RlbXAsICJfIiksICJbWyIsIDQpCiAgICBjb25kX3RlbXAgPC0gcGFzdGUoY29uZF9wdDEsIGNvbmRfcHQyLCBjb25kX3B0Mywgc2VwPSJfIikKCiAgICBpZihhbnkoZ3JlcGwoIm5haXZlIiwgY29uZF90ZW1wKSkpIAogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCAibmFpdmUiLCBzZXA9Il8iKQogICAgaWYoYW55KGdyZXBsKCJ1bnRyZWF0ZWQiLCBjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICJuYWl2ZSIsIHNlcD0iXyIpCiAgICBpZihhbnkoZ3JlcGwoIlBMWCIsY29uZF90ZW1wKSkpIAogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCBzYXBwbHkoc3Ryc3BsaXQoY29uZF90ZW1wLCAiXyIpLCAiW1siLCAzKSwgc2VwPSJfIikKICAgIGlmKGFueShncmVwbCgiZnJlZSIsY29uZF90ZW1wKSkpIAogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCAiOGRheTh1TVBMWENhMmZyZWUiLCBzZXA9Il8iKQogICAgaWYoYW55KGdyZXBsKCJDYTJjb250YWluaW5nIixjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICIzZGF5OHVNUExYX3dfQ2EyIiwgc2VwPSJfIikKICAgIGlmKGFueShncmVwbCgiTUVLIixjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICIzMG1pbk1FS2kiLCBzZXA9Il8iKQogICAgCgogICAgZCA8LSByZWFkLmNzdihmcGF0aCkKICAgIGQgPC0gY29udmVydFRpbWUoZCkKICAgIGNvbG5hbWVzKGQpIDwtIGZpeENvbG5hbWVzKGNvbG5hbWVzKGQpKQogICAgZCA8LSBkWyxpbnRlcnNlY3Qoc3RkX2NvbG5hbWVzLGNvbG5hbWVzKGQpKV0KICAgIGQkZXhwdF9kYXRlIDwtIGV4cHRfZGF0ZQogICAgZCRjb25kIDwtIGNvbmQKICAgIHJldHVybihkKQp9CgpzYW1wbGVST0lzIDwtIGZ1bmN0aW9uKGNvbmRpdGlvbiwgaWRzPXJvaXMsIG49MTAwKSBzYW1wbGUoaWRzW2dyZXAoY29uZGl0aW9uLGlkcyldLG4pCgpwbG90UENjYXRjaCA8LSBmdW5jdGlvbihyb2ksIGdyb3VwX2lkcz1jbHVzdCRjbHVzdGVyLCAuLi4pIHsKICAgIGdyb3VwcyA8LSB1bmlxdWUoZ3JvdXBfaWRzKQogICAgbXljb2xvcnMgPC0gZGljaHJvbWF0Ojpjb2xvcnNjaGVtZXMkQ2F0ZWdvcmljYWwuMTJbc2VxX2Fsb25nKGdyb3VwcyldCiAgICBwbG90KHBjX2NhdGNoW1sneCddXVssMV0sIHBjX2NhdGNoW1sneCddXVssMl0sIHR5cGU9Im4iLAogICAgICAgICB4bGFiPSJQQzEiLCB5bGFiPSJQQzIiLCAuLi4pCiAgICBzYXBwbHkoZ3JvdXBzLCBmdW5jdGlvbihncm91cCkgcG9pbnRzKHBjX2NhdGNoW1sneCddXVt3aGljaChncm91cF9pZHM9PWdyb3VwICYgcm9pcyAlaW4lIHJvaSksMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwY19jYXRjaFtbJ3gnXV1bd2hpY2goZ3JvdXBfaWRzPT1ncm91cCAmIHJvaXMgJWluJSByb2kpLDJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPW15Y29sb3JzW21hdGNoKGdyb3VwLGdyb3VwcyldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwY2g9MTksIGNleD0wLjUsKSkKICAgIHJldHVybihpbnZpc2libGUoTlVMTCkpCn0KCmBgYAojIyMgRGF0YXNldHMKMjAyMjA0MjUgPSBuYWl2ZSAgCjIwMjMwNDI2ID0gdW50cmVhdGVkICAKMjAyMTExMTQgPSB0b2xlcmFudD8gMyBkYXlzPyAgCjIwMjIwMzIxX2Z1cmEyX0EzNzVIMmJSRlBfNGRheTh1TVBMWAoyMDIyMDMyMV9mdXJhMl9BMzc1SDJiUkZQXzVkYXk4dU1QTFgKMjAyMzA0MjZfaWRsaW5nQ2EyZnJlZV9wdWxzaW5nMDAyX1YwMgoKYGBge3IgTG9hZCBhbmQgY29tcGlsZSBkYXRhfQpmcGF0aHMgPC0gYygiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vc2NsL2ZpL3lwcnQ2aDRzMmtocjdzazBrYW9hdi8yMDIzMDQyNl91bnRyZWF0ZWRfcHVsc2luZ19tb2QuY3N2P3Jsa2V5PWJ0NTc1eXZiNjEyY2dmMWJpMjNmYjluOTkmZGw9MSIsCiAgICAgICAgICAgICJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zY2wvZmkvZmswZmgxdHJyNDZhcm9tZnFkMzFrLzIwMjIwMzIxX2Z1cmEyX0EzNzVIMmJSRlBfNWRheTh1TVBMWC5jc3Y/cmxrZXk9amhobTgyMXQ4cTYzYzNuNnliNnVkYXN4ZyZkbD0xIiwKICAgICAgICAgICAgImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3NjbC9maS9yMGJpdHZ1NGxqejE5amFoOWY5cDgvMjAyMjAzMjFfZnVyYTJfQTM3NUgyYlJGUF83ZGF5OHVNUExYLmNzdj9ybGtleT1pNndueW80OHo1YTB2NG92c3JneXN6MnhtJmRsPTEiLAogICAgICAgICAgICAiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vc2NsL2ZpLzlqNmR2azl2bmFyM2NuNXhlMjRoZS8yMDIyMDMyMV9mdXJhMl9BMzc1SDJiUkZQXzEwZGF5OHVNUExYLmNzdj9ybGtleT02Mmw1cXBpaDRvZXR3cnRlMGszYmszc3pyJmRsPTEiLAogICAgICAgICAgICAiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vc2NsL2ZpLzZwcTRkNjJzZXIwYmNzdXZ3ZmNwdi8yMDIzMDQyNl9pZGxpbmdDYTJmcmVlX3B1bHNpbmcwMDJfVjAyX21vZC5jc3Y/cmxrZXk9azE5d3hpZm1maDNybjY5emRnbjBrYjN0YyZkbD0xIgogICAgICAgICAgICApCgpyYXdkYXRhcGF0aHMgPC0gYygiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vc2NsL2ZpL2YwYXA5dHV3ZmxkdHJqazJwZDN5bi8yMDIyMDQyNV9GdXJhMl9uYWl2ZTAwMi54bHN4P3Jsa2V5PWR4MXp3ajZvdjh6d3pxdDk0NGp3YzA1ejkmZGw9MSIsCiAgICAgICAgICAgICAgICAgICJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zY2wvZmkvcnUxY3Vuc3VxZ2k2OTF1YTA1ZGdzLzIwMjIwNDI1X0Z1cmEyX25haXZlMDAzLnhsc3g/cmxrZXk9ZjFxN3VoNDU0c3Fqc3BtcjNhbmFiZHkxayZkbD0xIgogICAgICAgICAgICAgICAgICApCgpmcCA8LSAiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy9lbHdjazNjbnB6dmc0dWYvMjAyMTExMTRfQTM3NV9DYTIlMkJfcHVsc2luZ19kYXRhLmNzdj9kbD0xIgoKZCA8LSByZWFkLmNzdihmcCkKY29sbmFtZXMoZCkgPC0gYygicmVnX2lkIiwgImZ1cmFfcmF0aW8iLCAidGltZV9zZWMiLCAidGltZV9taW4iKQpkJGV4cHRfZGF0ZSA8LSAiMjAyMTExMTQiCmQkaW1faWR4IDwtIG1hdGNoKGQkdGltZV9zZWMsIHNvcnQodW5pcXVlKGQkdGltZV9zZWMpKSkKY29sbmFtZXMoZCkgPC0gZml4Q29sbmFtZXMoY29sbmFtZXMoZCkpCmQgPC0gZFssaW50ZXJzZWN0KHN0ZF9jb2xuYW1lcyxjb2xuYW1lcyhkKSldCmQkY29uZCA8LSAiMjAyMTExMTRfMTRkYXk4dU1QTFgiCgpkIDwtIHJiaW5kKGQsIGRvLmNhbGwocmJpbmQsIGxhcHBseShmcGF0aHMsIHByZXByb2Nlc3NEYXRhKSkpCmQgPC0gcmJpbmQoZCwgZG8uY2FsbChyYmluZCwgbGFwcGx5KHJhd2RhdGFwYXRocywgcHJvY2Vzc1Jhd1hMU1gpKSkKCiMgY2FsY3VsYXRlL2FkanVzdCBjb2x1bW5zCmQkcmVnX2lkIDwtIHBhc3RlKGQkY29uZCwgZCRyZWdfaWQsIHNlcD0iXyIpCmQkaW50ZW5zaXR5X21lYW4gPC0gZCRmdXJhX3JhdGlvCgojIGVuc3VyZSBhbGwgdGltZXMgYXJlIGEgbXVsdGlwbGUgb2YgNQpkJHRpbWVfc2VjIDwtIERlc2NUb29sczo6Um91bmRUbyhkJHRpbWVfc2VjLCBtdWx0aXBsZT01KQpgYGAKCgoKIyMjIE5PVEUKRXhwZXJpbWVudHMgYXJlIGVhY2ggb2YgZGlmZmVyZW50IGR1cmF0aW9uLiBUbyBmYWNpbGl0YXRlIGludGVycHJldGF0aW9uLCB1c2UgbnVtYmVyIG9mIHRpbWUgcG9pbnRzIG9mIHRoZSBzaG9ydGVzdCBkYXRhc2V0IGZvciBlYWNoCgpgYGB7cn0KZXhwdF9kYXRlcyA8LSB1bmlxdWUoZCRleHB0X2RhdGUpCmNvbmRpdGlvbnMgPC0gdW5pcXVlKGQkY29uZCkKCm5faW1faWR4IDwtIG1pbihzYXBwbHkoY29uZGl0aW9ucywgZnVuY3Rpb24oY28pIGxlbmd0aCh1bmlxdWUoKGRbZCRjb25kPT1jbywiaW1faWR4Il0pKSkpKQoKdGVtcCA8LSBsYXBwbHkodW5pcXVlKGQkY29uZCksIGZ1bmN0aW9uKGNvKSB7CiAgICBhIDwtIGRbZCRjb25kPT1jbyxdCiAgICBhIDwtIGFbYSR0aW1lX3NlYyAlaW4lIHRhaWwodW5pcXVlKGEkdGltZV9zZWMpLCBuX2ltX2lkeCksXQogICAgYSR0aW1lX3NlYyA8LSBEZXNjVG9vbHM6OlJvdW5kVG8oYSR0aW1lX3NlYyAtIG1pbihhJHRpbWVfc2VjKSwgbXVsdGlwbGU9NSkKICAgICMgYSR0aW1lX3NlYyA8LSBzZXEoMCwoKG5faW1faWR4LTEpKjUpLDUpCiAgICBhJGltX2lkeCA8LSBtYXRjaChhJHRpbWVfc2VjLCBzb3J0KHVuaXF1ZShhJHRpbWVfc2VjKSkpCiAgICByb3duYW1lcyhhKSA8LSBOVUxMCiAgICByZXR1cm4oYSkKfSkKCmQgPC0gZG8uY2FsbChyYmluZCwgdGVtcCkKYGBgCgoKYGBge3J9CnJvaXMgPC0gdW5pcXVlKGQkcmVnX2lkKQp0aW1lcyA8LSB1bmlxdWUoZCR0aW1lX3NlYykKCiMgZnIgPSBmdXJhMl9yYXRpbwpmciA8LSBkYXRhLmZyYW1lKG1hdHJpeChkJGZ1cmFfcmF0aW8sIG5jb2w9bGVuZ3RoKHJvaXMpKSkKY29sbmFtZXMoZnIpIDwtIGFzLmNoYXJhY3Rlcihyb2lzKQoKIyBsaW5lYXIgbW9kZWwgLSBpbnRlcnNlY3QKbCA8LSBkby5jYWxsKGMsIGxhcHBseShjb25kaXRpb25zLCBmdW5jdGlvbihjbykgewogICAgYXBwbHkoZnJbLGdyZXBsKGNvLGNvbG5hbWVzKGZyKSldLCAyLCBmdW5jdGlvbih4KSBjb2VmKGxtKHggfiB0aW1lcykpWzJdKQp9KSkKCgojIG1lYW4gZnVyYV9yYXRpbwpmcl9tZWFuIDwtIGRvLmNhbGwoY2JpbmQsbGFwcGx5KGNvbmRpdGlvbnMsIGZ1bmN0aW9uKGNvKSB7CiAgICBhcHBseShmclssZ3JlcGwoY28sY29sbmFtZXMoZnIpKV0sIDEsIG1lYW4pCn0pKQpjb2xuYW1lcyhmcl9tZWFuKSA8LSBjb25kaXRpb25zCgojIGZybiA9IGZ1cmEyX3JhdGlvIG5vcm1hbGl6ZWQgKG1lYW4tY2VudGVyZWQpCmZybiA8LSBkby5jYWxsKGNiaW5kLCBsYXBwbHkoY29uZGl0aW9ucywgZnVuY3Rpb24oY28pIHsKICAgIGFwcGx5KGZyWyxncmVwbChjbyxjb2xuYW1lcyhmcikpXSwgMiwgZnVuY3Rpb24oeCkgeCAtIGZyX21lYW5bLG1hdGNoKGNvLGNvbmRpdGlvbnMpXSkKfSkpCgpkJGZ1cmFfcmF0aW9fbm9ybSA8LSBhcy52ZWN0b3IoZnJuKQpkJGludF9tZWFuX25vcm0gPC0gYXMudmVjdG9yKGZybikKYGBgCgoKIyMjIFRoaW5ncyB0byBjb25zaWRlcgoqIERUVyAgCiogU3VtIG9mIHRpbWUgYWJvdmUgdGhyZXNob2xkICAKKiBTdHJldGNoZXMgb2YgdGltZXMgYWJvdmUgdGhyZXNob2xkCiogVGltZXMgYmV0d2VlbiBzcGlrZXMKKiBOdW1iZXIgb2Ygc3Bpa2VzCiogRGV0ZXJtaW5lIHRoZSBiYWNrZ3JvdW5kIGZvciBlYWNoIHRyYWNlCgppbnRlcmVzdGluZyBST0lzOiAxMSwgMTIsIDE4LCAyMCwgMjUsIDI3CmBgYHtyfQpjaGVja3RoZXNlIDwtIHBhc3RlKCIyMDIxMTExNF8xNGRheTh1TVBMWCIsIGMoMTEsIDEyLCAxOCwgMjAsIDI1LCAyNyksIHNlcD0iXyIpCnBsb3QoZnVyYV9yYXRpbyB+IHRpbWVfc2VjLCBkYXRhPWRbZCRyZWdfaWQgJWluJSBjaGVja3RoZXNlLF0sIHR5cGU9Im4iLCB5bGltPWMoMCwxKSkKZm9yKHJvaSBpbiBjaGVja3RoZXNlKSBsaW5lcyhmdXJhX3JhdGlvIH4gdGltZV9zZWMsIGRhdGE9ZFtkJHJlZ19pZD09cm9pLF0sIGNvbD1kaWNocm9tYXQ6OmNvbG9yc2NoZW1lcyRDYXRlZ29yaWNhbC4xMlttYXRjaChyb2ksY2hlY2t0aGVzZSldKQpgYGAKCmBgYHtyfQpzcGlrZV9oZWlnaHQgPC0gc2QoZCRmdXJhX3JhdGlvX25vcm0pKjIuNQpyb2lfbGlzdCA8LSBsYXBwbHkodW5pcXVlKGQkcmVnX2lkKSwgZnVuY3Rpb24ocm9pKSAKICAgIHsKICAgICAgICBkYXQgPC0gZFtkJHJlZ19pZD09cm9pLCJmdXJhX3JhdGlvX25vcm0iXQogICAgICAgIHRocmVzaCA8LSAuMTUKICAgICAgICBvdXQgPC0gZGF0YS5mcmFtZShyZWdfaWQ9cm9pLAogICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW49bWVhbihkYXQpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNkPXNkKGRhdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgYXZnLnNwaWtlLmxlbmd0aD1hdmdTcGlrZUxlbmd0aChkYXQsIHRocmVzaG9sZD10aHJlc2gpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG51bS5ydW5zPW51bVJ1bnMoZGF0LCB0aHJlc2hvbGQ9dGhyZXNoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBudW0uc3Bpa2VzPW51bVNwaWtlcyhkYXQsdGhyZXNob2xkPXRocmVzaCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2xvcGU9Y29lZihsbShkYXQgfiB0aW1lcykpWzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyY2VwdD1jb2VmKGxtKGRhdCB+IHRpbWVzKSlbMV0KICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgcm93bmFtZXMob3V0KSA8LSBOVUxMCiAgICAgICAgcmV0dXJuKG91dCkKICAgIH0pCgpzdW1zdGF0cyA8LSBkby5jYWxsKHJiaW5kLCByb2lfbGlzdCkKc3Vtc3RhdHMkYXZnLnNwaWtlLmxlbmd0aFtpcy5uYShzdW1zdGF0cyRhdmcuc3Bpa2UubGVuZ3RoKV0gPC0gMAoKY2F0Y2ggPC0gbGFwcGx5KHVuaXF1ZShkJHJlZ19pZCksIGZ1bmN0aW9uKHJvaSkgCiAgICB7CiAgICAgICAgZGF0IDwtIGNhdGNoMjJfYWxsKGRbZCRyZWdfaWQ9PXJvaSwiZnVyYV9yYXRpb19ub3JtIl0pCiAgICAgICAgZGF0IDwtIGFzLmRhdGEuZnJhbWUoZGF0KQogICAgICAgIHJvd25hbWVzKGRhdCkgPC0gZGF0WywxXQogICAgICAgIGRhdCRuYW1lcyA8LSBOVUxMCiAgICAgICAgZGF0IDwtIHQoZGF0KQoKICAgICAgICByZXR1cm4oZGF0KQogICAgfSkKCmNhdGNoIDwtIGRvLmNhbGwocmJpbmQsIGNhdGNoKQpyb3duYW1lcyhjYXRjaCkgPC0gdW5pcXVlKGQkcmVnX2lkKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CnBjX2NhdGNoIDwtIHByY29tcChzY2FsZShjYXRjaCkpCnBsb3QocGNfY2F0Y2hbWyd4J11dWywxXSwgcGNfY2F0Y2hbWyd4J11dWywyXSwgcGNoPTE5LCBjZXg9MC41KQpgYGAKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0KYmlwbG90KHBjX2NhdGNoLCBjZXg9MC41KQpgYGAKCgoKYGBge3J9CnN1bXN0YXRzX3NjYWxlZCA8LSBzdW1zdGF0cwpzdW1zdGF0c19zY2FsZWRbLC0xXSA8LSBzY2FsZShzdW1zdGF0c19zY2FsZWRbLC0xXSkKCmxvX3NpZ25hbCA8LSBzdWJzZXQoc3Vtc3RhdHMsIG1lYW48MC4wNSAmIHNkPDAuMDEpCmxvX2lkcyA8LSBsb19zaWduYWwkcmVnX2lkCm1lc3NhZ2UocGFzdGUoIlRoZXJlIGFyZSIsbnJvdyhsb19zaWduYWwpLCJjZWxscyB3aXRoIHZlcnkgbG93IGFjdGl2aXR5IikpCmBgYAojIyMgQ2F0Y2gtMjIgdmFyaWFibGVzCkxvY2F0aW9uIG9mIGxvdy1zaWduYWwgdHJhY2VzIGluIENhdGNoLTIyIFBDQSBzcGFjZQpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpwbG90KHBjX2NhdGNoW1sneCddXVssMV0sIHBjX2NhdGNoW1sneCddXVssMl0sIHBjaD0xOSwgY2V4PTAuNSwgeGxhYj0iUEMxIiwgeWxhYj0iUEMyIikKcG9pbnRzKHBjX2NhdGNoW1sneCddXVtsb19zaWduYWwkcmVnX2lkLDFdLCBwY19jYXRjaFtbJ3gnXV1bbG9fc2lnbmFsJHJlZ19pZCwyXSwgcGNoPTE5LCBjZXg9MC41LCBjb2w9InllbGxvdyIpCgpgYGAKIyMgQm91bmRhcmllcyBvZiBDYXRjaC0yMiBQQ0Egc3BhY2UKSURzIG9uIHRoZSBib3VuZGFyaWVzIChtYW51YWxseSBpZGVudGlmaWVkIGZyb20gYmlwbG90IGFib3ZlKQo2MDQsIDUyMiwgMjEwLCA4NzAsIDk0NiwgNTM5LCA1NDIsIDc0NCwgNjc1CgoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KZXBzX3ZhbCA8LSAyCmNsdXN0IDwtIGRic2Nhbjo6ZGJzY2FuKHBjX2NhdGNoW1sneCddXSwgZXBzPWVwc192YWwsIG1pblB0cyA9IDIwKQoKbXljb2xzIDwtIHJldihkaWNocm9tYXQ6OmNvbG9yc2NoZW1lcyRDYXRlZ29yaWNhbC4xMilbc2VxKGxlbmd0aCh1bmlxdWUoY2x1c3QkY2x1c3RlcikpKV0KCnBsb3QocGNfY2F0Y2hbWyd4J11dWywxXSwgcGNfY2F0Y2hbWyd4J11dWywyXSwgcGNoPTE5LCBjZXg9MC41LCAKICAgICBjb2w9bXljb2xzW2NsdXN0JGNsdXN0ZXIrMV0sCiAgICAgeGxhYj0iUEMxIiwgeWxhYj0iUEMyIikKbXRleHQocGFzdGUoImVwcz0iLGVwc192YWwsInByb2R1Y2VkIixsZW5ndGgodW5pcXVlKGNsdXN0JGNsdXN0ZXIpKSwiY2x1c3RlcnMiKSwgMykKbGVnZW5kKC0xMiw2LGxlZ2VuZD1wYXN0ZSgiY2x1c3QiLHNlcShsZW5ndGgodW5pcXVlKGNsdXN0JGNsdXN0ZXIpKSkpLCBjb2w9bXljb2xzLCBwY2g9MTksIGNleD0wLjUpCmBgYAoKYGBge3J9Cm1lc3NhZ2UoY2F0KCJXaXRoIGVwcz0iLGVwc192YWwsIiwiLGxlbmd0aCh1bmlxdWUoY2x1c3QkY2x1c3RlcikpLCAiY2x1c3RlcnMgd2VyZSBpZGVudGlmaWVkIikpCmRpcHJhdGU6Om5FYWNoKGNsdXN0JGNsdXN0ZXIpCmBgYAoKIyMjIE5vIHNwaWtlcyBsb29rcyBsaWtlIGNsdXN0ZXI9PTEKYGBge3J9CnNldC5zZWVkKDEwKQpjaGVja3RoZXNlIDwtIHNhbXBsZShyb2lzW2NsdXN0JGNsdXN0ZXI9PTFdLCAxNSkKcGxvdFRyYWNlRnVyYShjaGVja3RoZXNlLCBkKQpgYGAKIyMjIENvbXBhcmUgdG8gbG9faWRzCmBgYHtyfQpwbG90VHJhY2VGdXJhKGxvX2lkc1sxOjE1XSwgZCkKYGBgCmBgYHtyfQphbGxfbm9fc3Bpa2VfaWRzIDwtIHVuaW9uKHJvaXNbY2x1c3QkY2x1c3Rlcj09MV0sbG9faWRzKQpsZW5ndGgoYWxsX25vX3NwaWtlX2lkcykKYGBgCgojIyMgb3RoZXJzICh3aXRoIHNwaWtlcz8pIGNsdXN0ZXI9PTAKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0Kc2V0LnNlZWQoNikKY2hlY2t0aGVzZSA8LSBzYW1wbGUocm9pc1tjbHVzdCRjbHVzdGVyPT0wXSwgNjApCnBsb3RUcmFjZUZ1cmEoY2hlY2t0aGVzZVsxOjE1XSwgZCkKYGBgCgojIyMgQ3JpdGVyaWEKUGhpbGlwIGRlc2NyaWJlZCBhIGZldyBiYXNpYyB0eXBlcyBvZiB0cmFjZXMgaGUgc2VlczoKCjEpIG5vIHNwaWtlcyAgCjEpIHBlcmlvZGljIHNwaWtpbmcgKG9zY2lsbGF0b3J5KSAgCjEpIHNwb3JhZGljIGluZGl2aWR1YWwgc3Bpa2luZyAocmFyZSBhbmQgcmFuZG9tKSAgCjEpIHNwb3JhZGljIHN1c3RhaW5lZCBzcGlraW5nICAKMSkgc3VzdGFpbmVkIGFjdGl2YXRpb24gKHR3by1sZXZlbCB0cmFjZSkgIAoKCiMjIyBBbGdvcml0aG1zIGZvciBjbGFzc2lmaWNhdGlvbgoKRmFzdCBmb3VyaWVyIHRyYW5zZm9ybSAgdG8gZGV0ZWN0IHJlZ3VsYXIgb3NjaWxsYXRvcnkgcGF0dGVybnMKYGBge3J9CmYgPC0gbXZmZnQoYXMubWF0cml4KGZybikpCgpvc2NpbCA8LSBzYXBwbHkocm9pcywgZnVuY3Rpb24ocm9pKSBhbnkoUmUoZlssYXMuY2hhcmFjdGVyKHJvaSldKVszOihucm93KGYpLTMpXSA+IDEwKSkKcm9pX29zY2lsIDwtIHJvaXNbb3NjaWxdCmBgYAoKYGBge3J9CnBsb3Qoc2VxKDIsbnJvdyhmKS0xKSwgdGFpbChoZWFkKFJlKGZbLHJvaV9vc2NpbFsyXV0pLC0xKSwtMSksIHR5cGU9J2wnLCB5bGltPWMoLTEsMjApKQpgYGAKCiMjIyBFeGFtcGxlIG9zY2lsbGF0b3J5IHRyYWNlcwpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD02fQpwbG90VHJhY2VGdXJhKHJvaV9vc2NpbFsxMToyMF0sIGQpCmBgYAoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KcGxvdChwY19jYXRjaFtbJ3gnXV1bLDFdLCBwY19jYXRjaFtbJ3gnXV1bLDJdLCBwY2g9MTksIGNleD0wLjUsIAogICAgIGNvbD1jKCJibHVlIiwib3JhbmdlIilbKHN1bXN0YXRzJHJlZ19pZCAlaW4lIHJvaV9vc2NpbCkrMV0sCiAgICAgeGxhYj0iUEMxIiwgeWxhYj0iUEMyIikKCmBgYAoKIyMjIEV4YW1wbGUgdHJhY2VzIHdpdGggaGkgbWVhbiB2YWx1ZXMKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0KaGkgPC0gc3Vtc3RhdHNfc2NhbGVkW3N1bXN0YXRzX3NjYWxlZCRtZWFuID49MC4xICYgc3Vtc3RhdHNfc2NhbGVkJHNkIDw9IDAsInJlZ19pZCJdCgpwbG90VHJhY2VGdXJhKGhpWzE6MTBdLCBkKQpgYGAKCgojIFNpbmdsZSBzcGlrZXMKYGBge3J9CnNpbmdsZV9zcGlrZSA8LSBzdW1zdGF0c1tzdW1zdGF0cyRudW0uc3Bpa2VzPT0xLCJyZWdfaWQiXQpteWNvbHMzIDwtIHZpcmlkaXNMaXRlOjp2aXJpZGlzKG49MzgpCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTZ9CnBsb3RUcmFjZUZ1cmEoc2luZ2xlX3NwaWtlWzE6MjBdLCBkKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9CmhpX3Nsb3BlIDwtIHJvaXNbd2hpY2gobD40ZS01KV0KcGxvdFRyYWNlRnVyYShoaV9zbG9wZVsxOjEwXSwgZCkKCmBgYAoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KcGxvdChwY19jYXRjaFtbJ3gnXV1bLDFdLCBwY19jYXRjaFtbJ3gnXV1bLDJdLCBwY2g9MTksIGNleD0wLjUsIAogICAgIGNvbD1jKCJibHVlIiwib3JhbmdlIilbKHN1bXN0YXRzJHJlZ19pZCAlaW4lIGhpX3Nsb3BlKSsxXSwKICAgICB4bGFiPSJQQzEiLCB5bGFiPSJQQzIiKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CnBsb3QocGNfY2F0Y2hbWyd4J11dWywxXSwgcGNfY2F0Y2hbWyd4J11dWywyXSwgcGNoPTE5LCBjZXg9MC41LCB4bGFiPSJQQzEiLCB5bGFiPSJQQzIiKQpwb2ludHMocGNfY2F0Y2hbWyd4J11dW3NpbmdsZV9zcGlrZSwxXSwgcGNfY2F0Y2hbWyd4J11dW3NpbmdsZV9zcGlrZSwyXSwgcGNoPTE5LCBjZXg9MC41LCBjb2w9InllbGxvdyIpCgpgYGAKIyMjIEV4cHQgY29uZGl0aW9ucwpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpwbG90KHBjX2NhdGNoW1sneCddXVssMV0sIHBjX2NhdGNoW1sneCddXVssMl0sIHBjaD0iLiIsIGNleD0wLjUsIAogICAgIGNvbD1yZXYoZGljaHJvbWF0Ojpjb2xvcnNjaGVtZXMkQ2F0ZWdvcmljYWwuMTIpW21hdGNoKGdzdWIoIl9bMC05XXsxLDN9JCIsICIiLCBzdW1zdGF0cyRyZWdfaWQpLGNvbmRpdGlvbnMpXSwKICAgICB4bGFiPSJQQzEiLCB5bGFiPSJQQzIiKQpsZWdlbmQoLTEzLDgsIGNvbmRpdGlvbnMsIGNvbD1kaWNocm9tYXQ6OmNvbG9yc2NoZW1lcyRDYXRlZ29yaWNhbC4xMltzZXFfYWxvbmcoY29uZGl0aW9ucyldLCBjZXg9MC41LCBwY2g9MTkpCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CnNldC5zZWVkKDEzKQpteV9zYW1wbGVzIDwtIGludmlzaWJsZShsYXBwbHkoY29uZGl0aW9ucywgZnVuY3Rpb24oY29uZCkgewogICAgYSA8LSBzYW1wbGVST0lzKGNvbmRpdGlvbj1jb25kKQogICAgcGxvdFBDY2F0Y2goYSwgY2x1c3QkY2x1c3RlciwgbWFpbj1jb25kKQogICAgYQp9KSkKYGBgCgoKYGBge3J9CnNldC5zZWVkKDEzKQpteV9zYW1wbGVzIDwtIGludmlzaWJsZShsYXBwbHkoY29uZGl0aW9ucywgZnVuY3Rpb24oY29uZCkgewogICAgYSA8LSBzYW1wbGVST0lzKGNvbmRpdGlvbj1jb25kKQogICAgcGxvdFBDY2F0Y2goYSwgYXMuaW50ZWdlcighcm9pcyAlaW4lIGFsbF9ub19zcGlrZV9pZHMpLCBtYWluPWNvbmQpCiAgICBhCn0pKQpgYGAKCgoKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0KbGFwcGx5KHNlcV9hbG9uZyhteV9zYW1wbGVzKSwgZnVuY3Rpb24oaSkgcGxvdFRyYWNlRnVyYShteV9zYW1wbGVzW1tpXV1bIW15X3NhbXBsZXNbW2ldXSAlaW4lIGFsbF9ub19zcGlrZV9pZHNdWzE6MjBdLCBkKSkKYGBgCgojIyMgRXhhbXBsZSB0cmFjZXMKRmlndXJlIDFCCmBgYHtyIGZpZy5oZWlnaHQ9My41LCBmaWcud2lkdGg9NX0KZXhlbXBsYXJfaWRzIDwtIGMoICAiMjAyMTExMTRfMTRkYXk4dU1QTFhfMTA1IiwKICAgICAgICAgICAgICAgICAgICAiMjAyMTExMTRfMTRkYXk4dU1QTFhfODAyIiwKICAgICAgICAgICAgICAgICAgICAiMjAyMTExMTRfMTRkYXk4dU1QTFhfMjk3IiwKICAgICAgICAgICAgICAgICAgICAiMjAyMjAzMjFfN2RheTh1TVBMWF8zNDAiLAogICAgICAgICAgICAgICAgICAgICIyMDIyMDMyMV83ZGF5OHVNUExYXzExMjEiLAogICAgICAgICAgICAgICAgICAgICIyMDIxMTExNF8xNGRheTh1TVBMWF82MDQiLAogICAgICAgICAgICAgICAgICAgICIyMDIxMTExNF8xNGRheTh1TVBMWF8yMCIKKQoKc3Bpa2VfZXhhbXBsZXMgPC0gZFtkJHJlZ19pZCAlaW4lIGV4ZW1wbGFyX2lkcywgXQpzcGlrZV9leGFtcGxlcyRyZWdfaWQgPC0gZmFjdG9yKHNwaWtlX2V4YW1wbGVzJHJlZ19pZCwgbGV2ZWxzPWV4ZW1wbGFyX2lkcykKCnBsb3RUcmFjZUZ1cmEoZXhlbXBsYXJfaWRzLCBzcGlrZV9leGFtcGxlcywgeWw9YygwLjUsMS4yNSkpCgojIGdnc2F2ZSgiZXhhbXBsZV9mdXJhMl90cmFjZXMucG5nIiwgZGV2aWNlPSJwbmciLCB3aWR0aD01LCBoZWlnaHQ9My41LCB1bml0cz0iaW4iKQpgYGAKCgojIyMgUGxvdCAlIGluIGVhY2ggY2x1c3RlciBmb3IgZWFjaCBjb25kaXRpb24KUGVyZm9ybSBib290c3RyYXBwaW5nIHRvIGdldCBzYW1wbGUgc3RhdGlzdGljcy4gQXNzZXNzaW5nIHByb3BvcnRpb25zIG9mIGNlbGxzIGluIGNsdXN0ZXJzIDAgYW5kIDEgCgpgYGB7cn0KIyBmaWx0ZXJlZCBvYmplY3RzIGluIG5haXZlIGRhdGEgKHNob3VsZCBmaWx0ZXIgYWxsIGRhdGFzZXRzKSB3aXRoIGFyZWEgPiAxMDAgwrVNXjIgCgpzZXQuc2VlZCgxMykKCnByb3Bfc3Bpa2luZyA8LSBkby5jYWxsKGMsIGxhcHBseShjb25kaXRpb25zLCBmdW5jdGlvbihjbykgewogICAgc2FtcCA8LSBsYXBwbHkoMToxMDAsIGZ1bmN0aW9uKGkpIHNhbXBsZShyb2lzW2dyZXBsKGNvLHJvaXMpXSwyMDAsIHJlcGxhY2U9VFJVRSkpCiAgICBzYXBwbHkoc2FtcCwgZnVuY3Rpb24oeCkgbGVuZ3RoKHdoaWNoKCF4ICVpbiUgYWxsX25vX3NwaWtlX2lkcykpLzIwMCkKfSkpCgpwcm9wX3NwaWtpbmdfZGYgPC0gZGF0YS5mcmFtZShjb25kaXRpb249cmVwKGNvbmRpdGlvbnMsIGVhY2g9MTAwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3Bfc3Bpa2luZz1wcm9wX3NwaWtpbmcpCgpwcm9wX3NwaWtpbmdfY29uZF9kZiA8LSBwcm9wX3NwaWtpbmdfZGYKcHJvcF9zcGlraW5nX2NvbmRfZGYkZGF0ZSA8LSAgc2FwcGx5KHN0cnNwbGl0KHByb3Bfc3Bpa2luZ19jb25kX2RmJGNvbmRpdGlvbiwgIl8iKSwgIltbIiwgMSkKcHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uIDwtICBzYXBwbHkoc3Ryc3BsaXQocHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uLCAiXyIpLCAiW1siLCAyKQpwcm9wX3NwaWtpbmdfY29uZF9kZiRjb25kaXRpb24gPC0gIGdzdWIoImRheTh1TVBMWCIsIiIscHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uKQpwcm9wX3NwaWtpbmdfY29uZF9kZltncmVwKCJuYWl2ZSIscHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uKSwiY29uZGl0aW9uIl0gPC0gIDAKcHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uIDwtICBmYWN0b3IocHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uLCBsZXZlbHM9YygiMCIsICI1IiwiNyIsIjEwIiwiMTQiKSkKcHJvcF9zcGlraW5nX2NvbmRfZGYgPC0gcHJvcF9zcGlraW5nX2NvbmRfZGZbcHJvcF9zcGlraW5nX2NvbmRfZGYkZGF0ZSAhPSAiMjAyMzA0MjYiLF0KYGBgCgojIyMgVGltZSBkZXBlbmRlbmNlIG9mIHNwaWtpbmcgYmVoYXZpb3IKRmlndXJlIDFDCmBgYHtyIFZpb2xpbiBwbG90LCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpwIDwtIGdncGxvdChkYXRhPXByb3Bfc3Bpa2luZ19jb25kX2RmLCBhZXMoeD1jb25kaXRpb24sIHk9cHJvcF9zcGlraW5nLCBmaWxsPTEpKSArIAogICAgZ2VvbV92aW9saW4od2lkdGg9Ljc1KSArIGdlb21fYm94cGxvdCh3aWR0aD0wLjEsIGNvbG9yPSJncmV5IiwgYWxwaGE9MC41KSArIAogICAgIyBjb29yZF9mbGlwKCkgKyAKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE2KSwKICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE4LCBmYWNlPSJib2xkIiksCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCwgZmFjZT0iYm9sZCIpKSArIAogICAgeWxpbShjKDAsMC43NSkpICsKICAgIHlsYWIoIlByb3BvcnRpb24gc3Bpa2luZyIpICsgCiAgICB4bGFiKCJEYXlzIGluIEJSQUZpIikKcAoKIyBnZ3NhdmUoIkJSQUZpX3RpbWVfcHJvcF9zcGlraW5nLnBuZyIsIGRldmljZT0icG5nIiwgd2lkdGg9NCwgaGVpZ2h0PTQsIHVuaXRzPSJpbiIpCmBgYAoKCmBgYHtyfQptIDwtIGxtKHByb3Bfc3Bpa2luZyB+IGNvbmRpdGlvbiwgZGF0YT1wcm9wX3NwaWtpbmdfY29uZF9kZikKYSA8LSBhb3YobSkKdHVrZXkgPC0gVHVrZXlIU0QoYSkKdHVrZXkkY29uZGl0aW9uCmBgYAoKCiMjIyBOdW1iZXIgb2YgY2VsbHMgcGVyIGNvbmRpdGlvbgpgYGB7cn0Kc2FwcGx5KGNvbmRpdGlvbnMsIGZ1bmN0aW9uKGNvKSBsZW5ndGgodW5pcXVlKHJvaXNbZ3JlcChjbyxyb2lzKV0pKSkKYGBgCgoKCiMgQW5hbHlzaXMgb2YgZXh0cmFjZWxsdWxhciBjYWxjaXVtIG9uIGxvbmctdGVybSBCUkFGaS10cmVhdGVkIGNlbGwgc3Bpa2luZyBiZWhhdmlvcgojIyBTdXBwbGVtZW50YXJ5IEZpZ3VyZSBTNAojIyMgQW5hbHlzaXMgb2YgbWFudWFsIHF1YW50aWZpY2F0aW9uIG9mIDggZGF5IEJSQUZpIHcvbyBjYWxjaXVtClBlcmZvcm1lZCBieSBQaGlsaXAuIFdpbGwgYXBwZW5kIG1hbnVhbGx5IGFzc2Vzc2VkIG5vLXNwaWtlIGByZWdfaWRgcyB0byBgYWxsX25vX3NwaWtlX2lkc2AuCgpgYGB7cn0Kbm9DYV84ZF9tYW51YWwgPC0gcmVhZC5jc3YoIi4uL2RhdGEvMjAyMzA0MjZfOGRCUkFGaWNhMmZyZWVfc2FtcGxlX2lkcy5jc3YiKQp0ZXN0cm9pcyA8LSBjKG5vQ2FfOGRfbWFudWFsJHJlZ19pZCwgcm9pc1tncmVwbCgiMjAyMjAzMjFfN2RheTh1TVBMWCIscm9pcyldKQpgYGAKCgpgYGB7cn0Kc2V0LnNlZWQoMTMpCnRlc3Rjb25kcyA8LSBjKCIyMDIyMDMyMV83ZGF5OHVNUExYIiwiMjAyMzA0MjZfOGRheTh1TVBMWENhMmZyZWUiKQoKcHJvcF9zcGlraW5nX2NhIDwtIGRvLmNhbGwoYywgbGFwcGx5KHRlc3Rjb25kcywgZnVuY3Rpb24oY28pIHsKICAgIHNhbXAgPC0gbGFwcGx5KDE6MTAwLCBmdW5jdGlvbihpKSBzYW1wbGUodGVzdHJvaXNbZ3JlcGwoY28sdGVzdHJvaXMpXSwgMjAwLCByZXBsYWNlID0gVFJVRSkpCiAgICBzYXBwbHkoc2FtcCwgZnVuY3Rpb24oeCkgbGVuZ3RoKHdoaWNoKCF4ICVpbiUgYyhhbGxfbm9fc3Bpa2VfaWRzLG5vQ2FfOGRfbWFudWFsJHJlZ19pZFtub0NhXzhkX21hbnVhbCRzcGlrZXM9PTBdKSkpLzIwMCkKfSkpCgpwcm9wX3NwaWtpbmdfY2FfZGYgPC0gZGF0YS5mcmFtZShjb25kaXRpb249cmVwKGMoIkNhMisiLCJubyBDYTIrIiksIGVhY2g9MTAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcF9zcGlraW5nPXByb3Bfc3Bpa2luZ19jYSkKCmBgYAoKCiMjIyA4IGRheXMgQlJBRmkgdy9vIENhMisgaW4gYnVmZmVyIHZzIDcgZGF5cyBCUkFGaSB3LyBDYTIrIGluIGJ1ZmZlcgpTdXBwbGVtZW50YXJ5IEZpZ3VyZSBTNApgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0yfQpwIDwtIGdncGxvdChkYXRhPXByb3Bfc3Bpa2luZ19jYV9kZiwgYWVzKHg9Y29uZGl0aW9uLCB5PXByb3Bfc3Bpa2luZywgZmlsbD0xKSkgKyAKICAgIGdlb21fdmlvbGluKHdpZHRoPS43NSkgKyAKICAgIGdlb21fYm94cGxvdCh3aWR0aD0wLjEsIGNvbG9yPSJncmV5IiwgYWxwaGE9MC41KSArIAogICAgIyBjb29yZF9mbGlwKCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTYsIGZhY2U9ImJvbGQiKSwKICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE2LCBmYWNlPSJib2xkIikpICsgCiAgICAjIHlsaW0oYygwLDAuNzUpKSArCiAgICB5bGFiKCJQcm9wb3J0aW9uIHNwaWtpbmciKSArIAogICAgeGxhYigiIikgCnAKYGBgCgo=